OPC Studio User's Guide and Reference
Role of the data type ID
Concepts > OPC Data Client Concepts > OPC Data Client Extensions > OPC Data Client Integrated Extensions > OPC UA Complex Data Extension > Basic OPC UA Complex Data operations > Role of the data type ID

The data type ID (in DataTypeId Property) is an optional information in the UAGenericObject Class (it equals to UAModelNodeDescriptor.Null when "not present"). It is basically a node ID of data type that this UAGenericObject represents.

Show UAGenericObject class diagram

For reads, writes and subscriptions (but not for method calls or some other scenarios), the data type ID of certain node can be determined from the information model of the server (by reading the DataType attribute of that node). That is, however, the declared data type for that node. As the OPC UA information model for data types follows the classic paradigms of OOP (Object Oriented Programming), the actual data type at runtime can also be any direct or indirect sub-type of the declared data type (in fact, when the declared data type is abstract, the actual data type must be some sub-type, different from the declared type - this is again the same as in classic OOP, because instances of abstract types cannot be created).

In many cases, the node in the OPC UA server you are dealing with has a data type that has no sub-types. In such case, you are lucky, because you always know upfront the precise data type that is to be used for the node's value. But in some cases, the situation is more complicated, and the problem domain calls for sub-types, a hierarchical structure of data types. The picture below is a class diagram that shows example relations between three data types. The ScalarValueDataType and ArrayValueDataType inherit from a base data type, Structure.

 

When a node in such server then specifies its data type as Structure, there are three possible data types that the value can actually have: Structure, ScalarValueDataType, or ArrayValueDataType.

When the generic object is obtained from the server (such as by reads and subscriptions), the component always fills in the with a data type ID that is not UAModelNodeDescriptor.Null. When the generic object is to be transferred to the server (such as by writing), the component will use the data type ID provided, if it is not equal to UAModelNodeDescriptor.Null. Otherwise, the component will try to determine the data type (ID) itself, which may not work in case the actual data to be written are a sub-type of the node's data type (and which is always the case if the node's data type is abstract).

What do you need to take out of this discussion? That's actually easy:

How you obtain the data type ID depends on the application. For example

 

The example below determines sub-types of a given data type:

.NET

// This example shows how to retrieve all sub-types of a given data type, recursively.
//
// Find all latest examples here: https://opclabs.doc-that.com/files/onlinedocs/OPCLabs-OpcStudio/Latest/examples.html .
// OPC client and subscriber examples in C# on GitHub: https://github.com/OPCLabs/Examples-QuickOPC-CSharp .
// Missing some example? Ask us for it on our Online Forums, https://www.opclabs.com/forum/index ! You do not have to own
// a commercial license in order to use Online Forums, and we reply to every post.

using System;
using OpcLabs.BaseLib.OperationModel.Generic;
using OpcLabs.EasyOpc.UA;
using OpcLabs.EasyOpc.UA.AddressSpace.Standard;
using OpcLabs.EasyOpc.UA.Extensions;

namespace UADocExamples._EasyUAClient
{
    class RetrieveAllDataTypes
    {
        public static void Main1()
        {
            UAEndpointDescriptor endpointDescriptor =
                "opc.tcp://opcua.demo-this.com:51210/UA/SampleServer";
            // or "http://opcua.demo-this.com:51211/UA/SampleServer" (currently not supported)
            // or "https://opcua.demo-this.com:51212/UA/SampleServer/"

            // Instantiate the client object
            var client = new EasyUAClient();

            // Retrieve all sub-types of the 'Structure' data type.
            ValueResult<UANodeDescriptor[]> result = client.RetrieveAllDataTypes(
                endpointDescriptor, UADataTypeIds.Structure);
            // Check if the operation succeeded. Use the ThrowIfFailed method instead if you want exception be thrown.
            if (!result.Succeeded)
            {
                Console.WriteLine("*** Failure: {0}", result.ErrorMessageBrief);
                return;
            }

            // Display results. Note that all node descriptors have node IDs in them; but the default display format shows 
            // the browse paths, which are more readable, when they are available.
            foreach (UANodeDescriptor nodeDescriptor in result.Value)
                Console.WriteLine(nodeDescriptor);

            // Example output:
            //
            //NodeId="Structure"
            //BrowsePath="[Structure]/Argument"
            //BrowsePath="[Structure]/StatusResult"
            //BrowsePath="[Structure]/UserTokenPolicy"
            //BrowsePath="[Structure]/ApplicationDescription"
            //BrowsePath="[Structure]/EndpointDescription"
            //BrowsePath="[Structure]/UserIdentityToken"
            //BrowsePath="[Structure]/EndpointConfiguration"
            //BrowsePath="[Structure]/SupportedProfile"
            //BrowsePath="[Structure]/BuildInfo"
            //BrowsePath="[Structure]/SoftwareCertificate"
            //BrowsePath="[Structure]/SignedSoftwareCertificate"
            //BrowsePath="[Structure]/AddNodesItem"
            //BrowsePath="[Structure]/AddReferencesItem"
            //BrowsePath="[Structure]/DeleteNodesItem"
            //BrowsePath="[Structure]/DeleteReferencesItem"
            //BrowsePath="[Structure]/ScalarTestType"
            //BrowsePath="[Structure]/ArrayTestType"
            //BrowsePath="[Structure]/CompositeTestType"
            //BrowsePath="[Structure]/RegisteredServer"
            //BrowsePath="[Structure]/ContentFilterElement"
            //BrowsePath="[Structure]/ContentFilter"
            //BrowsePath="[Structure]/FilterOperand"
            //BrowsePath="[Structure]/HistoryEvent"
            //BrowsePath="[Structure]/MonitoringFilter"
            //BrowsePath="[Structure]/RedundantServerDataType"
            //BrowsePath="[Structure]/SamplingIntervalDiagnosticsDataType"
            //BrowsePath="[Structure]/ServerDiagnosticsSummaryDataType"
            //BrowsePath="[Structure]/ServerStatusDataType"
            //BrowsePath="[Structure]/SessionDiagnosticsDataType"
            //BrowsePath="[Structure]/SessionSecurityDiagnosticsDataType"
            //BrowsePath="[Structure]/ServiceCounterDataType"
            //BrowsePath="[Structure]/SubscriptionDiagnosticsDataType"
            //BrowsePath="[Structure]/ModelChangeStructureDataType"
            //BrowsePath="[Structure]/Range"
            //BrowsePath="[Structure]/EUInformation"
            //BrowsePath="[Structure]/Annotation"
            //BrowsePath="[Structure]/ProgramDiagnosticDataType"
            //BrowsePath="[Structure]/SemanticChangeStructureDataType"
            //BrowsePath="[Structure]/HistoryEventFieldList"
            //BrowsePath="[Structure]/AggregateConfiguration"
            //BrowsePath="[Structure]/EnumValueType"
            //BrowsePath="[Structure]/TimeZoneDataType"
            //BrowsePath="[Structure]/EndpointUrlListDataType"
            //BrowsePath="[Structure]/NetworkGroupDataType"
            //BrowsePath="[Structure]/AxisInformation"
            //BrowsePath="[Structure]/XVType"
            //BrowsePath="[Structure]/ComplexNumberType"
            //BrowsePath="[Structure]/DoubleComplexNumberType"
            //BrowsePath="[Structure]/ScalarValueDataType"
            //BrowsePath="[Structure]/ArrayValueDataType"
            //BrowsePath="[Structure]/UserScalarValueDataType"
            //BrowsePath="[Structure]/UserArrayValueDataType"
            //BrowsePath="[Structure]/UserIdentityToken/AnonymousIdentityToken"
            //BrowsePath="[Structure]/UserIdentityToken/UserNameIdentityToken"
            //BrowsePath="[Structure]/UserIdentityToken/X509IdentityToken"
            //BrowsePath="[Structure]/UserIdentityToken/IssuedIdentityToken"
            //BrowsePath="[Structure]/FilterOperand/ElementOperand"
            //BrowsePath="[Structure]/FilterOperand/LiteralOperand"
            //BrowsePath="[Structure]/FilterOperand/AttributeOperand"
            //BrowsePath="[Structure]/FilterOperand/SimpleAttributeOperand"
            //BrowsePath="[Structure]/MonitoringFilter/EventFilter"
        }
    }
}
' This example shows how to retrieve all sub-types of a given data type, recursively.
'
' Find all latest examples here: https://opclabs.doc-that.com/files/onlinedocs/OPCLabs-OpcStudio/Latest/examples.html .
' OPC client and subscriber examples in VB.NET on GitHub: https://github.com/OPCLabs/Examples-QuickOPC-VBNET .
' Missing some example? Ask us for it on our Online Forums, https://www.opclabs.com/forum/index ! You do not have to own
' a commercial license in order to use Online Forums, and we reply to every post.

Imports OpcLabs.BaseLib.OperationModel.Generic
Imports OpcLabs.EasyOpc.UA
Imports OpcLabs.EasyOpc.UA.AddressSpace.Standard
Imports OpcLabs.EasyOpc.UA.Extensions

Namespace _EasyUAClient
    Friend Class RetrieveAllDataTypes
        Public Shared Sub Main1()

            Dim endpointDescriptor As UAEndpointDescriptor =
                "opc.tcp://opcua.demo-this.com:51210/UA/SampleServer"
            ' or "http://opcua.demo-this.com:51211/UA/SampleServer" (currently not supported)
            ' or "https://opcua.demo-this.com:51212/UA/SampleServer/"

            ' Instantiate the client object
            Dim client = New EasyUAClient()

            ' Retrieve all sub-types of the 'Structure' data type.
            Dim result As ValueResult(Of UANodeDescriptor()) = client.RetrieveAllDataTypes( _
                endpointDescriptor, UADataTypeIds.Structure)
            ' Check if the operation succeeded. Use the ThrowIfFailed method instead if you want exception be thrown.
            If Not result.Succeeded Then
                Console.WriteLine("*** Failure: {0}", result.ErrorMessageBrief)
            End If

            ' Display results. Note that all node descriptors have node IDs in them; but the default display format shows 
            ' the browse paths, which are more readable, when they are available.
            For Each nodeDescriptor As UANodeDescriptor In result.Value
                Console.WriteLine(nodeDescriptor)
            Next nodeDescriptor

            ' Example output:
            '
            'NodeId="Structure"
            'BrowsePath="[Structure]/Argument"
            'BrowsePath="[Structure]/StatusResult"
            'BrowsePath="[Structure]/UserTokenPolicy"
            'BrowsePath="[Structure]/ApplicationDescription"
            'BrowsePath="[Structure]/EndpointDescription"
            'BrowsePath="[Structure]/UserIdentityToken"
            'BrowsePath="[Structure]/EndpointConfiguration"
            'BrowsePath="[Structure]/SupportedProfile"
            'BrowsePath="[Structure]/BuildInfo"
            'BrowsePath="[Structure]/SoftwareCertificate"
            'BrowsePath="[Structure]/SignedSoftwareCertificate"
            'BrowsePath="[Structure]/AddNodesItem"
            'BrowsePath="[Structure]/AddReferencesItem"
            'BrowsePath="[Structure]/DeleteNodesItem"
            'BrowsePath="[Structure]/DeleteReferencesItem"
            'BrowsePath="[Structure]/ScalarTestType"
            'BrowsePath="[Structure]/ArrayTestType"
            'BrowsePath="[Structure]/CompositeTestType"
            'BrowsePath="[Structure]/RegisteredServer"
            'BrowsePath="[Structure]/ContentFilterElement"
            'BrowsePath="[Structure]/ContentFilter"
            'BrowsePath="[Structure]/FilterOperand"
            'BrowsePath="[Structure]/HistoryEvent"
            'BrowsePath="[Structure]/MonitoringFilter"
            'BrowsePath="[Structure]/RedundantServerDataType"
            'BrowsePath="[Structure]/SamplingIntervalDiagnosticsDataType"
            'BrowsePath="[Structure]/ServerDiagnosticsSummaryDataType"
            'BrowsePath="[Structure]/ServerStatusDataType"
            'BrowsePath="[Structure]/SessionDiagnosticsDataType"
            'BrowsePath="[Structure]/SessionSecurityDiagnosticsDataType"
            'BrowsePath="[Structure]/ServiceCounterDataType"
            'BrowsePath="[Structure]/SubscriptionDiagnosticsDataType"
            'BrowsePath="[Structure]/ModelChangeStructureDataType"
            'BrowsePath="[Structure]/Range"
            'BrowsePath="[Structure]/EUInformation"
            'BrowsePath="[Structure]/Annotation"
            'BrowsePath="[Structure]/ProgramDiagnosticDataType"
            'BrowsePath="[Structure]/SemanticChangeStructureDataType"
            'BrowsePath="[Structure]/HistoryEventFieldList"
            'BrowsePath="[Structure]/AggregateConfiguration"
            'BrowsePath="[Structure]/EnumValueType"
            'BrowsePath="[Structure]/TimeZoneDataType"
            'BrowsePath="[Structure]/EndpointUrlListDataType"
            'BrowsePath="[Structure]/NetworkGroupDataType"
            'BrowsePath="[Structure]/AxisInformation"
            'BrowsePath="[Structure]/XVType"
            'BrowsePath="[Structure]/ComplexNumberType"
            'BrowsePath="[Structure]/DoubleComplexNumberType"
            'BrowsePath="[Structure]/ScalarValueDataType"
            'BrowsePath="[Structure]/ArrayValueDataType"
            'BrowsePath="[Structure]/UserScalarValueDataType"
            'BrowsePath="[Structure]/UserArrayValueDataType"
            'BrowsePath="[Structure]/UserIdentityToken/AnonymousIdentityToken"
            'BrowsePath="[Structure]/UserIdentityToken/UserNameIdentityToken"
            'BrowsePath="[Structure]/UserIdentityToken/X509IdentityToken"
            'BrowsePath="[Structure]/UserIdentityToken/IssuedIdentityToken"
            'BrowsePath="[Structure]/FilterOperand/ElementOperand"
            'BrowsePath="[Structure]/FilterOperand/LiteralOperand"
            'BrowsePath="[Structure]/FilterOperand/AttributeOperand"
            'BrowsePath="[Structure]/FilterOperand/SimpleAttributeOperand"
            'BrowsePath="[Structure]/MonitoringFilter/EventFilter"
        End Sub
    End Class
End Namespace

Python

# This example shows how to retrieve all sub-types of a given data type, recursively.
#
# Find all latest examples here: https://opclabs.doc-that.com/files/onlinedocs/OPCLabs-OpcStudio/Latest/examples.html .
# OPC client and subscriber examples in Python on GitHub: https://github.com/OPCLabs/Examples-QuickOPC-Python .
# Missing some example? Ask us for it on our Online Forums, https://www.opclabs.com/forum/index ! You do not have to own
# a commercial license in order to use Online Forums, and we reply to every post.
# The QuickOPC package is needed. Install it using "pip install opclabs_quickopc".
import opclabs_quickopc

# Import .NET namespaces.
from OpcLabs.EasyOpc.UA import *
from OpcLabs.EasyOpc.UA.AddressSpace.Standard import *
from OpcLabs.EasyOpc.UA.Extensions import *
from OpcLabs.EasyOpc.UA.OperationModel import *


endpointDescriptor = UAEndpointDescriptor('opc.tcp://opcua.demo-this.com:51210/UA/SampleServer')
# or 'http://opcua.demo-this.com:51211/UA/SampleServer' (currently not supported)
# or 'https://opcua.demo-this.com:51212/UA/SampleServer/'

# Instantiate the client object.
client = EasyUAClient()

# Retrieve all sub-types of the 'Structure' data type.
valueResult = IEasyUAClientExtension2.RetrieveAllDataTypes(client,
                                                           endpointDescriptor,
                                                           UANodeDescriptor(UADataTypeIds.Structure))
# Check if the operation succeeded. Use the ThrowIfFailed method instead if you want exception be thrown.
if not valueResult.Succeeded:
    print('*** Failure: ', valueResult.ErrorMessageBrief, sep='')
    exit()

# Display results. Note that all node descriptors have node IDs in them; but the default display format shows
# the browse paths, which are more readable, when they are available.
for nodeDescriptor in valueResult.Value:
    print(nodeDescriptor)

print()
print('Finished.')

 

 

 Note that this example shows both abstract and concrete data types. Only the concrete (= non-abstract) data types can be used for data transfer. You can determine whether a data type is abstract by reading the IsAbstract attribute of its data type ID node.

See Also

Reference